import { Plugin, UserConfig } from 'vite';
import * as vite from 'vite';

/**
 * Sets up test config for Vitest
 * and downlevels any dependencies that use
 * async/await to support zone.js testing
 * and tests w/fakeAsync
 */
export function angularVitestPlugin(): Plugin {
  return {
    name: '@analogjs/vitest-angular-esm-plugin',
    apply: 'serve',
    enforce: 'post',
    config(userConfig) {
      return {
        optimizeDeps: {
          include: ['tslib'],
        },
        ssr: {
          noExternal: [
            '@analogjs/vitest-angular/setup-testbed',
            /fesm2022(.*?)testing/,
            /fesm2015/,
          ],
        },
        test: {
          pool: (userConfig as any).test?.pool ?? 'vmThreads',
        },
      };
    },
    async transform(_code, id) {
      if (
        (/fesm2022/.test(id) && _code.includes('async ')) ||
        _code.includes('@angular/cdk')
      ) {
        if (vite.rolldownVersion) {
          const { code, map } = await vite.transformWithOxc(_code, id, {
            lang: 'js',
            target: 'es2016',
            sourcemap: true,
          });

          return {
            code,
            map,
          };
        } else {
          const { code, map } = await vite.transformWithEsbuild(_code, id, {
            loader: 'js',
            format: 'esm',
            target: 'es2016',
            sourcemap: true,
            sourcefile: id,
          });

          return {
            code,
            map,
          };
        }
      }

      return undefined;
    },
  };
}

/**
 * This eagerly disables esbuild so Vitest
 * disables it when its internal plugin
 * is configured.
 */
export function angularVitestEsbuildPlugin(): Plugin {
  return {
    name: '@analogjs/vitest-angular-esbuild-oxc-plugin',
    enforce: 'pre',
    config(userConfig: UserConfig) {
      if (vite.rolldownVersion) {
        return {
          oxc: userConfig.oxc ?? false,
        };
      }

      return {
        esbuild: userConfig.esbuild ?? false,
      };
    },
  };
}

/**
 * This plugin does post-processing with esbuild
 * instead of preprocessing to re-align
 * the sourcemaps so breakpoints and coverage reports
 * work correctly.
 */
export function angularVitestSourcemapPlugin(): Plugin {
  return {
    name: '@analogjs/vitest-angular-sourcemap-plugin',
    async transform(code: string, id: string) {
      if (!/\.ts/.test(id)) {
        return;
      }

      const [, query] = id.split('?');

      if (query && query.includes('inline')) {
        return;
      }

      if (vite.rolldownVersion) {
        const result = await vite.transformWithOxc(code, id, {
          lang: 'js',
        });

        return result as unknown as vite.TransformResult;
      } else {
        const result = await vite.transformWithEsbuild(code, id, {
          loader: 'js',
        });

        return result;
      }
    },
  };
}

export function angularVitestPlugins() {
  return [
    angularVitestPlugin(),
    angularVitestEsbuildPlugin(),
    angularVitestSourcemapPlugin(),
  ];
}
